package com.api.phm.service.impl;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.api.common.config.ApiConfig;
import com.api.common.core.redis.RedisCache;
import com.api.common.domain.model.RuleLatestDataQuery;
import com.api.common.utils.DateUtils;
import com.api.phm.domain.model.AlaRuleDevModel;
import com.api.phm.domain.query.AlarmLogQuery;
import com.api.phm.domain.query.alarmRule.AlarmRuleCheck;
import com.api.phm.domain.query.alarmRule.AlarmRuleQuery;
import com.api.phm.domain.vo.AlarmLogVo;
import com.api.phm.domain.vo.AlarmRuleQueryListVo;
import com.api.phm.domain.vo.AlarmRuleVo;
import com.api.phm.mapper.AlarmRuleMapper;
import com.api.phm.service.IAlarmRuleService;
import com.api.phm.service.IFaultLogService;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

@Service
public class AlarmRuleServiceImpl implements IAlarmRuleService {

    @Autowired
    private AlarmRuleMapper alarmRuleMapper;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IFaultLogService faultLogService;

    /**
     * 根据信息查找对应规则列表
     *
     * @param alarmRuleVo 需要查询的规则条件
     * @return 规则列表
     */
    @Override
    public List<AlarmRuleQuery> searchList(AlarmRuleVo alarmRuleVo) {
        return alarmRuleMapper.searchList(alarmRuleVo);
    }

    @Override
    public List<AlarmRuleQuery> searchList2(AlarmRuleQueryListVo vo) {
        if (ObjectUtils.allNotNull(vo.getDeviceMap()) && ObjectUtils.allNotNull(vo.getRuleMold())) {
            vo.initQueryTab();
        }
//        return alarmRuleMapper.searchList2(alarmRuleVo, vTable);
        return null;
    }

    /**
     * 通过信息确认规则
     *
     * @param alarmRuleVo 告警规则查询信息
     * @return 对应告警规则
     */
    @Override
    public AlarmRuleQuery searchItem(AlarmRuleVo alarmRuleVo) {
        return alarmRuleMapper.searchItem(alarmRuleVo);
    }

    /**
     * 添加告警规则
     *
     * @param alarmRuleVo 告警规则内容
     * @return 告警规则（包含最新ID）
     */
    @Override
    public AlarmRuleQuery add(AlarmRuleVo alarmRuleVo) {
        Long ruleId = alarmRuleMapper.add(alarmRuleVo);
        alarmRuleVo.setRuleId(ruleId);
        return alarmRuleMapper.searchItem(alarmRuleVo);
    }

    @Override
    public List<AlarmLogQuery> searchAlarmLog(AlarmLogVo alarmLogVo) {
        return alarmRuleMapper.searchAlarmLog(alarmLogVo);
    }

    @Override
    public AlarmLogQuery searchAlarmLogItem(AlarmLogVo alarmLogVo) {
        return alarmRuleMapper.searchAlarmLogItem(alarmLogVo);
    }

    @Override
    public Long addAlarmLog(AlarmLogVo alarmLogVo) {
        return alarmRuleMapper.addAlarmLog(alarmLogVo);
    }

    @Override
    public void updateAlarmReadMark(Long alarmId) {
        alarmRuleMapper.updateAlarmReadMark(alarmId);
    }

    /**
     * 更新告警规则
     *
     * @param alarmRuleVo 告警规则信息
     */
    @Override
    public void update(AlarmRuleVo alarmRuleVo) {
        alarmRuleMapper.update(alarmRuleVo);
    }

    /**
     * 删除告警规则
     *
     * @param ruleId 规则ID
     */
    @Override
    public void delete(Long ruleId) {
        alarmRuleMapper.deleteItem(ruleId);
    }

    /**
     * 添加缓存
     *
     * @param items
     */
    @Override
    public void addCacheRule(List<RuleLatestDataQuery> items) {
        alarmRuleMapper.addCacheRule(items);
        Map<String, RuleLatestDataQuery> map = ApiConfig.getRuleCache();
        for (RuleLatestDataQuery i : items) {
            map.replace(i.getKey(), i);
            redisCache.setCacheObject(i.getKey(), i);
        }
        ApiConfig.setRuleCache(map);
    }

    @Override
    public void refreshCacheRule() {
        Map<String, RuleLatestDataQuery> map = new HashMap<>();
        List<RuleLatestDataQuery> list = alarmRuleMapper.getCacheRule();
        for (RuleLatestDataQuery item : list) {
            map.put(item.getKey(), item);
            redisCache.setCacheObject(item.getKey(), item);
        }
        ApiConfig.setRuleCache(map);
    }

    /**
     * 获取缓存规则最新信息，如果为空则。获取数据库内信息
     *
     * @return 缓存规则最新信息
     */
    @Override
    public Map<String, RuleLatestDataQuery> getCacheRule() {
        Map<String, RuleLatestDataQuery> map = new HashMap<>();
        try {
            map = ApiConfig.getRuleCache();
            System.out.println("[ApiConfig.getRuleCache]:" + map);
            if (map == null || map.isEmpty()) {
                refreshCacheRule();
                map = ApiConfig.getRuleCache();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 检测所有告警
     */
    @Override
    public void checkAllRule() {
        final int noAlarmFlag = -1;
        AlarmRuleVo vo = new AlarmRuleVo();
//        vo.setRuleId(2L);
        List<AlarmRuleQuery> list = searchList(vo);
        for (AlarmRuleQuery query : list) {
            int flag = checkRule(query);
            /**
             * 没有告警
             */
            if (flag == noAlarmFlag) {
                continue;
            }
        }
    }

    private void mkAlarmRule(AlarmRuleQuery query, int flag) {
        Long ruleId = query.getRuleId();
        String ruleKey = "alarmRule_" + ruleId;
        String alarmRuleKey = "alarm_rule:alarmRule_" + ruleId;
//        Integer cacheFlag = redisCache.getCacheObject(ruleKey);
        Integer cacheFlag = redisCache.getCacheObject(alarmRuleKey);
        if (cacheFlag != null && cacheFlag.equals(flag)) {
            return;
        } else {
            AlarmLogVo alarmLogVo = new AlarmLogVo(query, flag);
            alarmRuleMapper.addAlarmLog(alarmLogVo);
//            redisCache.setCacheObject(ruleKey, flag);
            redisCache.setCacheObject(alarmRuleKey, flag);
        }

    }

    /**
     * 规则组检测放回弟几级告警
     *
     * @param model 告警
     * @return 触发等级；-1 没有触发
     */
    @Override
    public int checkRule(AlarmRuleQuery model) {
        int f = -1;
//        String alarmMess = model.getAlarmMessage();
        Map<String, AlaRuleDevModel> devJO = model.getAlaRuleDevModelMap();
        JSONArray rulesJson = model.getRules();
        for (int i = 0; i < rulesJson.size(); i++) {
            List<AlarmRuleCheck> rules = JSONArray.parseArray(rulesJson.getJSONArray(i).toString(), AlarmRuleCheck.class);
            if (loopRuleItem(rules, devJO)) {
//                System.out.println("checkRule :[RuleId]:" + model.getRuleId() + ";[RuleName]:" + model.getLocalRuleName() + ";[RuleLevel]:" + i);
                mkAlarmRule(model, i);
                return i;
            }
        }
        return f;
    }

    /**
     * 循环子项
     *
     * @param checks
     * @return
     */
    private Boolean loopRuleItem(List<AlarmRuleCheck> checks, Map<String, AlaRuleDevModel> devJO) {
        Boolean f = judgeRuleChild(checks.get(0), devJO);
        int size = checks.size();
//        int len = size - 1;
        if (size == 1) return f;
        for (int s = 1; s < size; s++) {
            AlarmRuleCheck arc = checks.get(s);
            String logic = arc.getLogic();
            Boolean c = judgeRuleChild(arc, devJO);
            f = (StringUtils.equals(logic, "or")) ? f || c : f && c;
        }
        return f;
    }

    /**
     * 判断规则子项
     *
     * @param arc   柜子子项
     * @param devJO 模型对象
     * @return
     */
    private Boolean judgeRuleChild(AlarmRuleCheck arc, Map<String, AlaRuleDevModel> devJO) {
        return (arc.getChildren() == null || arc.getChildren().isEmpty())
                ? judgeRule(arc, devJO.get(arc.getDevice()))
                : loopRuleItem(arc.getChildren(), devJO);
    }

    /**
     * 告警判断
     *
     * @param checkItem
     * @param devModel
     * @return
     */
    private Boolean judgeRule(AlarmRuleCheck checkItem, AlaRuleDevModel devModel) {
        String operator = checkItem.getOperator();
        String contrast = checkItem.getContrast();
        String pattern = "^(-?\\d+)(\\.\\d+)?";
        Pattern r = Pattern.compile(pattern);
        if (r.matcher(contrast).matches()) {
            if (StringUtils.equals("last", checkItem.getAggr())) {
                return judgeLast(checkItem);
            } else {
                return judgeMongo(checkItem, devModel);
            }
        } else {
            return ApiConfig.judgeStr(operator, redisCache.getCacheMap(devModel.getKey()).toString(), contrast);
        }
    }

    private JSONObject judgeRule2(AlarmRuleCheck checkItem, AlaRuleDevModel devModel) {
        String operator = checkItem.getOperator();
        String contrast = checkItem.getContrast();
        String pattern = "^(-?\\d+)(\\.\\d+)?";
        Pattern r = Pattern.compile(pattern);
        if (r.matcher(contrast).matches()) {
            if (StringUtils.equals("last", checkItem.getAggr())) {
                return judgeLast2(checkItem);
            } else {
                return judgeMongo2(checkItem, devModel);
            }
        } else {
            return ApiConfig.judgeStr2(operator, redisCache.getCacheMap(devModel.getKey()).toString(), contrast);
        }
    }

    /**
     * 均值、最大值、最小值，总值 比较校验
     *
     * @param item
     * @param devModel
     * @return
     */
    public Boolean judgeMongo(AlarmRuleCheck item, AlaRuleDevModel devModel) {
//        System.out.println(DateUtils.dateTimeNow("yyyy-MM-dd HH:mm:ss:SSS"));
        Date endTime = DateUtils.addHours(DateUtils.getNowDate(), 8);
        Date startTime = DateUtils.addMinutes(endTime, -item.getTimes());
        String collName = "takt_list";
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(new Criteria()
                        .andOperator(
                                Criteria.where("dateTime").gte(startTime),
                                Criteria.where("dateTime").lte(endTime)
                        )
                        .and("plcId").is(devModel.getPlcId())
                        .and("elementId").is(devModel.getElementId())
                        .and("datCol").is(devModel.getDatCol())
                ),
                Aggregation.group("plcId", "robotId", "elementId", "datCol")
                        .count().as("count")
                        .sum("taskData.evl1").as("sum")
                        .avg("taskData.evl1").as("avg")
                        .max("taskData.evl1").as("max")
                        .min("taskData.evl1").as("min")
                        .stdDevPop("taskData.evl1").as("vare")
        );
        try {
            List<Map> list = mongoTemplate.aggregate(aggregation, collName, Map.class).getMappedResults();
            if (list.isEmpty()) {
                return false;
            }
            Double r = Double.parseDouble(list.get(0).get(item.getAggr()).toString());
            return ApiConfig.judgeDouble(item.getOperator(), r, Double.parseDouble(item.getContrast()));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public JSONObject judgeMongo2(AlarmRuleCheck item, AlaRuleDevModel devModel) {
        JSONObject rj = new JSONObject();
        rj.put("plcId", devModel.getPlcId());
        rj.put("robotId", devModel.getRobotId());
        rj.put("elementId", devModel.getElementId());
        rj.put("datCol", devModel.getDatCol());
        Date endTime = DateUtils.addHours(DateUtils.getNowDate(), 8);
        Date startTime = DateUtils.addMinutes(endTime, -item.getTimes());
        String collName = "takt_list";
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(new Criteria()
                        .andOperator(
                                Criteria.where("dateTime").gte(startTime),
                                Criteria.where("dateTime").lte(endTime)
                        )
                        .and("plcId").is(devModel.getPlcId())
                        .and("elementId").is(devModel.getElementId())
                        .and("datCol").is(devModel.getDatCol())
                ),
                Aggregation.group("plcId", "robotId", "elementId", "datCol")
                        .count().as("count")
                        .sum("taskData.evl1").as("sum")
                        .avg("taskData.evl1").as("avg")
                        .max("taskData.evl1").as("max")
                        .min("taskData.evl1").as("min")
                        .stdDevPop("taskData.evl1").as("vare")
        );
        try {
            List<Map> list = mongoTemplate.aggregate(aggregation, collName, Map.class).getMappedResults();
            if (list.isEmpty()) {
                rj.put("val", null);
                rj.put("falg", false);
                return rj;
            }
            Double r = Double.parseDouble(list.get(0).get(item.getAggr()).toString());
            rj.put("val", r);
            rj.put("falg", ApiConfig.judgeDouble(item.getOperator(), r, Double.parseDouble(item.getContrast())));
            return rj;
        } catch (Exception e) {
            e.printStackTrace();
            rj.put("val", null);
            rj.put("falg", false);
            return rj;
        }
    }

    /**
     * 实时数据校验
     *
     * @param item
     * @return
     */
    private Boolean judgeLast(AlarmRuleCheck item) {
        String key = item.getDevice();
        RuleLatestDataQuery cache = redisCache.getCacheObject(key);
//        System.out.println("[cache]:" + Double.parseDouble(cache.getValue()) + ";[item]" + Double.parseDouble(item.getContrast()));
        return ApiConfig.judgeDouble(item.getOperator(), Double.parseDouble(cache.getValue()), Double.parseDouble(item.getContrast()));
    }

    private JSONObject judgeLast2(AlarmRuleCheck item) {
        JSONObject jo = new JSONObject();
        String key = item.getDevice();
        jo.put("key", key);
        RuleLatestDataQuery cache = redisCache.getCacheObject(key);
//        System.out.println("[cache]:" + Double.parseDouble(cache.getValue()) + ";[item]" + Double.parseDouble(item.getContrast()));
        jo.put("val", cache.getValue());
        jo.put("flag", ApiConfig.judgeDouble(item.getOperator(), Double.parseDouble(cache.getValue()), Double.parseDouble(item.getContrast())));
        return jo;
    }
}
